TechNote - OneNote ID Stability
October 3, 2026
OneNote IDs Are Not Stable Identifiers
Measured October 2026 on Windows desktop OneNote, using the COM API.
If you build anything that remembers a OneNote page, section, or notebook between sessions, such as an index, a bookmark, a tag store, or a cache, you will be tempted to key it on the ID attribute that OneNote hands you. Don't. Those IDs are handles for the current session of a notebook, not durable identities. This note documents how they behave, what survives, and a method for recognizing a page without relying on them.
The behavior described below was measured, not assumed. The design guidance is a recommendation based on those measurements. Where something was not measured, it is listed at the end.
Summary
|
Event |
Page IDs |
Section IDs |
Notebook ID |
Created time |
Last-modified time |
|
Close and reopen a notebook |
all change |
all change |
changes |
unchanged |
unchanged |
|
Move a page to another section |
that page changes |
unchanged |
unchanged |
unchanged |
unchanged |
|
Move a page to another notebook |
that page changes |
unchanged |
unchanged |
unchanged |
unchanged |
|
Edit a page's creation date |
unchanged |
unchanged |
unchanged |
changes |
unchanged |
Two consequences follow:
- An ID you saved yesterday may point at nothing today, with no error and no notification.
- A page's content and its created/modified times are far more stable than anything OneNote calls an ID.
Anatomy of an ID
Page IDs have a regular structure: {section-guid}{1}{page-specific-hex-tail}
- The leading GUID is the section's GUID. In the sample, every page ID contained its section's GUID, and the number of distinct leading GUIDs equaled the number of sections.
- The middle value was 1 for every page.
- The tail is 41 to 47 hex characters. Within the sample it was unique per page.
- Section and notebook IDs use the same braces-and-GUID shape, with a short suffix.
This is why moving a page changes its ID: the first half of the ID names the section it lives in. But as the next section shows, the tail is not preserved either.
What changes when a notebook is closed and reopened
Three notebooks were closed and reopened through the API (two cloud notebooks of 376 and 157 pages, and one local file-based notebook of 77 pages), and the hierarchy was captured before and after.
- Every page ID changed. Not just the section half; matching on the tail alone found none of the 533 cloud pages, so the page-specific part is regenerated too.
- Every section ID and the notebook ID changed, in the cloud notebooks and in the local one.
- Other open notebooks were untouched. In one snapshot of 980 pages across three notebooks, closing and reopening one notebook left the 604 pages in the other two with every ID unchanged.
- Names, titles, created times, modified times, page levels did not change.
- Reopened content appears gradually. In a cloud notebook the page count went 0 → 63 → 108 → 160 → … → 376 over roughly 90 seconds. A local notebook filled in within a few seconds. Anything that scans during that window sees a partial notebook.
The last point is easy to miss and has real consequences: if you treat "not in the hierarchy right now" as "deleted", a scan that happens during a reopen will conclude that most of the notebook was deleted.
What changes when a page is moved
A page was moved between sections by editing the hierarchy XML and calling UpdateHierarchy, which is how add-ins generally move pages.
- Only the one page moved changed its ID. In a 77-page notebook, 76 pages kept their IDs.
- Section IDs and the notebook ID were unchanged.
- The page's created time, modified time, title, and content were identical before and after. (A content hash of the extracted text matched exactly.)
- A page already stamped with a marker in its own metadata kept the marker, because the marker lives in the page content.
A move to a different notebook behaved identically: one page changed ID, everything else, including section and notebook IDs, was unchanged, and the created/modified times and content survived.
Moving a page into a section that already contains a page with the same creation timestamp is possible, and it happened here, which matters for the key design below.
What changes when someone edits the creation date
Some tools, and OneNote itself, let a page's creation date be edited. The date was changed by setting the page root's dateTime attribute through UpdatePageContent.
- The page ID did not change.
- lastModifiedTime did not change. A creation-date edit is invisible to any change detection based on modified time.
- The new date was reported in the hierarchy immediately.
So an edited date produces no signal. A system that stored the old created time as part of a page's identity will silently stop recognizing that page. When the edit is combined with an ID change, such as a notebook reopen afterwards, none of the signals involving created time match any more.
Edited dates exist in real data. In the sample, one page out of 980 had a creation time 2.4 hours later than its last-modified time, which is hard to explain without the date having been set by hand or by a tool that imported it.
Note: this was tested by changing the attribute through the API. Editing the date through the OneNote UI is expected to behave the same way, but was not measured separately.
How unique are the alternatives?
If IDs cannot identify a page, what can? Across 980 pages in three cloud notebooks:
|
Candidate signal |
Result |
|
Creation time (millisecond precision) |
Unique except for one pair of pages that shared an identical timestamp |
|
Title alone |
Not unique: 7 groups, 16 pages, shared a title within a single section |
|
Notebook + title + creation time |
No duplicates within any of the three notebooks |
|
Notebook + section + title + creation time |
No duplicates |
And in a notebook full of copied pages (a test notebook), 38 of 77 pages shared a creation timestamp with another page, and 39 shared a content hash. Copying a page preserves its title and creation time, so copies are indistinguishable from originals on hierarchy data alone. Some pages are simply identical, and no scheme can tell them apart.
How well do those signals recover a page after the IDs change?
After closing and reopening notebooks, each page that came back with a new ID was matched to its previous record using only hierarchy data. "Unique" means exactly one candidate matched.
|
Signal used to re-find the page |
Pages |
Unique |
Ambiguous |
Not found |
|
Notebook + section + title + created |
533 |
533 |
0 |
0 |
|
Title + created (any notebook) |
533 |
533 |
0 |
0 |
|
Created time alone |
533 |
531 |
2 |
0 |
|
Notebook + section + title |
533 |
529 |
4 |
0 |
|
Page ID tail |
533 |
0 |
0 |
533 |
|
Content hash (157-page notebook only) |
157 |
157 |
0 |
0 |
A moved page (new ID, new section) was found uniquely by notebook + title + created, by title + created, and by content hash, but not by anything that included the section. A page moved to another notebook was found by title + created and by content hash, but not by anything that included the notebook.
After an edited creation date plus a reopen, notebook + section + title and the content hash still found the page, and everything involving the created time did not.
A design that survives all of this
The measurements above suggest the following approach. It never trusts an ID beyond the current session, and it never writes anything into the user's page.
1. Treat OneNote IDs as session handles
Use an ID to navigate or load a page right now. Never use it as a database key, and never assume one saved from a previous session still resolves.
2. Give each page your own key
Assign a surrogate key in your own storage (an auto-incrementing integer is fine) and map the current OneNote ID onto it. Never reuse a key after deleting a record, otherwise data stored against the old key gets attached to a different page.
3. Build container keys from names, not IDs
Section and notebook IDs also change on reopen, so identify containers by name and path: for a notebook, its path (or name as a fallback); for a section, the names of its section groups plus its own name.
4. Re-find a page by a ladder of evidence, strongest first
Match each page, from hierarchy data only (no page loads), against the records that were not matched by ID:
- The same OneNote ID: use it, nothing changed.
- Same notebook, section, title, and creation time: a reopen.
- Same notebook, title, and creation time: moved to another section.
- Same title and creation time: moved to another notebook.
- Same notebook, section, and title: the creation time was edited. Treat this as a weak match: accept it, but re-read the page's content rather than trusting any cached data.
- Only if still unresolved, and only if there is something to compare, load the page and match on a hash of its text, or on a marker.
Each step considers only records and pages that earlier steps left unclaimed. That matters when a copy of a page exists beside its original: the original claims its record by ID first, so the copy is correctly a new page instead of being mistaken for the original.
5. Match only when it is one-to-one
If two pages are indistinguishable at a given step, do not guess. Decline the match and treat the pages as new, which costs a re-read but is never wrong. The one exception is pages that are identical in every respect including modified time: their content is the same, so it does not matter which record each one gets.
6. Do not delete on the first miss
Because a reopened notebook fills in gradually, mark a page as missing the first time it is not seen, keep its data, and delete it only after it has stayed missing for a grace period (hours, not seconds). If the page reappears under a new ID, the ladder above reconnects it to its record.
7. Never hold a skipped container against its pages
Locked or encrypted sections cannot be listed. Record that a section was skipped so its pages are not mistaken for deleted ones.
8. Refresh the created time every scan
Since an edit to the creation date changes no modified time, copy dateTime from the hierarchy on every pass instead of only when a page is re-read.
Why not write an ID into the page?
The obvious alternative is to stamp each page with a marker in its own metadata, which does travel with the page through reopens and moves. It has real costs:
- It is a write to a page the user did not edit. In practice, stamping pages this way bumps their last-modified times, so the user sees pages they never touched as recently modified. Hashtag Scanner marks pages this way the first time it sees them.
- Pages that are copied carry the marker with them, so markers are not unique and need their own collision handling.
- It does not help for pages you are not allowed or unable to write to, such as read-only notebooks or locked sections.
A stamp can still be useful as an extra, read-only hint (if a page already has one, it can confirm a match), but it should not be the foundation of identity.
What was not measured
- Paragraph (object) IDs. Whether the object IDs of paragraphs and other page elements survive a reopen was not measured. If you store links to specific paragraphs, test this before relying on them.
- Another machine. IDs are widely reported to differ between machines for the same synced notebook, which is consistent with them being regenerated on every open, but a cross-machine comparison was not part of these tests.
- Renaming a notebook, section, or page, and moving or renaming sections and section groups. Moving a page between section groups is expected to behave like moving it between sections, but was not tested separately.
- Encrypted or locked sections, whose pages cannot be loaded.
- The OneNote UI as the means of moving pages or editing dates. The tests used the COM API, and results through the UI are expected to match but were not compared.
- Other platforms (OneNote for Mac, web, or mobile), and the exact OneNote build. Behavior may differ.
- Very large notebooks. The largest notebook measured had 447 live pages.
Reproducing the measurements
The checks use only read operations. Note that GetHierarchy with the pages scope returns every page with its ID, name, dateTime, lastModifiedTime, and pageLevel, which is all the ladder above uses.
# Run in Windows PowerShell 5.1
$one = New-Object -ComObject OneNote.Application
# list the open notebooks (scope 2 = notebooks)
$xml = ''
$one.GetHierarchy('', 2, [ref]$xml)
$notebooks = ([xml]$xml).DocumentElement.ChildNodes | `
Where-Object { $_.LocalName -eq 'Notebook' }
# take all pages of one notebook (scope 4 = pages)
$xml = ''
$one.GetHierarchy($notebooks[0].ID, 4, [ref]$xml)
$pages = ([xml]$xml).SelectNodes("//*[local-name()='Page']")
# show the shape of the page IDs with the hex digits masked
$pages | ForEach-Object { $_.ID -replace '[0-9A-Fa-f]', 'X' } |
Group-Object | Sort-Object Count -Descending | Select-Object Count, Name -First 5
To reproduce the reopen result, save the page list (ID, section, title, dateTime) to a file, close and reopen the notebook, save it again, and compare: the IDs will differ and the other fields will not. Wait until the page count stops growing before the second capture.
#omwiki #omdeveloper #omtechnote
© 2026 Steven M Cohn. All rights reserved.
Please consider a sponsorship or one-time donation to support ongoing development
Created with OneNote.